#include<iostream>
#include<string>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
#include<sys/wait.h>
#include<sys/types.h>
using namespace std;
const int basesize=1024;
const int argvnum=64;
const int genvnum=64;
//全局参数列表
char* gargv[argvnum];
int gargc;
//全局的pwd【】和环境变量pwdenv
char pwd[basesize];
char pwdenv[basesize];
//自己的环境变量表
char* genv[genvnum];

string GetUserName()//获取用户名
{
   string name = getenv("USER");
   return name.empty() ? "None" : name;
}
string GetHostName()//获取主机名
{
   //string hostname = getenv("HOSTNAME");
   string hostname = "hcss-ecs-9307";
   return hostname.empty() ? "None" : hostname;

}
string GetPwd()//获取pwd
{
    if(nullptr==getcwd(pwd,sizeof(pwd))) return "None";
    //更新一下环境变量
    snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
    putenv(pwdenv);
    return pwd;
   // string pwd = getenv("PWD");
    //return pwd.empty() ? "None" : pwd;
}
string MakeCommandLine()//制作命令行
{
    //kxf@hcss-ecs-9307:~/112/112-linux/myshell$
    char command_line[basesize];
    snprintf(command_line,basesize,"[%s@%s:~%s]# ",\
        GetUserName().c_str(),GetHostName().c_str(),GetPwd().c_str());
       
        return command_line;
}
void PrintCommandLine()//打印命令行提示符
{
   printf("%s",MakeCommandLine().c_str());
   fflush(stdout);
}
bool GetCommandLine(char *command_buffer,int size)//获取用户命令
{
    //我们认为用户输入的命令行是一个字符串
    //“ls -a -l”
    char *result = fgets(command_buffer,size,stdin);
    if(!result)
    {
        return false;//获取失败
    }
    command_buffer[strlen(command_buffer)-1]=0;
    if(strlen(command_buffer)==0) return false;
    return true;
}
void ParseCommandLine(char *command_buffer,int len)//分析命令
{
   //初始化argv和argc
   memset(gargv,0,sizeof(gargv));
   gargc=0;
   //"ls -a -n"="ls","-a","-n"
   const char *sep=" ";
   gargv[gargc++]=strtok(command_buffer,sep);

   while(gargv[gargc++]=strtok(nullptr,sep));
   gargc--;
}
//有些命令要由子进程执行
//但有些命令必须由shell自己执行，即内键命令（Build Command）！比如cd/export/echo
//cd:如果子进程执行，那么只会改变子进程的工作目录，父进程没有变化
//export：环境变量是向下继承的，子进程增加环境变量不会影响父进程
//echo：本地变量表不会继承给子进程
bool ExecuteCommand()//执行命令
{
    //让子进程执行命令
    pid_t id = fork();
    if(id<0) return false;
    else if(id==0)
    {
        //子进程
        //exec执行
        execvpe(gargv[0],gargv,genv);
        //退出
        exit(1);
    }
    int status=0;
    pid_t rid = waitpid(id,nullptr,0);
    if(rid>0)
    {
    return true;
      
    }
    return false;
}
void AddEnv(const char* item)
{
    int index=0;
    while(genv[index]) index++;
    genv[index]=(char*)malloc(strlen(item)+1);
    strncpy(genv[index++],item,strlen(item)+1);
    genv[index]=nullptr;
}
bool CheckBuildCommand()//检测并执行内建命令
{
    if(strcmp(gargv[0],"cd")==0)
    {
        if(gargc==2)
        {
            chdir(gargv[1]);
        }
        return true;
    }
    else if(strcmp(gargv[0],"export")==0)
    {
        if(gargc==2)
        {
            AddEnv(gargv[1]);
        }
        return true;
    }
    else if(strcmp(gargv[0],"env")==0)
    {
       for(int i=0;genv[i];i++)
       {

        printf("%s\n",genv[i]);
       }
        return true;
    }
    return false;
}
//作为一个shell，环境变量应该从系统配置文件获取
//我们今天从父shell中获取环境变量
void InitEnv()
{
    extern char** environ;
    int i=0;
    while(environ[i])
    {
        genv[i]=(char*)malloc(strlen(environ[i])+1);
        strncpy(genv[i],environ[i],strlen(environ[i])+1);
        i++;
    }
    genv[i]=nullptr;
}
int main()
{
    InitEnv();
    char command_buffer[basesize];//输出型参数，存储用户输入的命令参数
    while(true)
    {
        PrintCommandLine();//打印命令行提示符
        //putchar('\n');
        if(!GetCommandLine(command_buffer,basesize)) continue;//获取用户命令
        
        ParseCommandLine(command_buffer,strlen(command_buffer));//分析命令
        if(CheckBuildCommand()) continue;//检测并执行内建命令
        ExecuteCommand();//执行命令
        //sleep(100);
    }
    
    return 0;
}

